package com.me.dogdog.util

import java.util.*

sealed interface KResult<T, E> {
    fun bool(): Boolean = this is Ok

    fun option(): T? = when (this) {
        is Err -> null
        is Ok -> value
    }

    fun <U> map(op: (T) -> U): KResult<U, E> = when (this) {
        is Err -> err(this)
        is Ok -> ok(op(value))
    }

    infix fun and(other: KResult<T, E>): KResult<T, E> = when (this) {
        is Err -> other
        is Ok -> this
    }

    infix fun or(other: KResult<T, E>): KResult<T, E> = when (this) {
        is Err -> other
        is Ok -> this
    }

    fun <U> andThen(op: (T) -> KResult<U, E>): KResult<U, E> = when (this) {
        is Err -> err(this)
        is Ok -> op(value)
    }

    fun <U> orElse(op: (E) -> KResult<T, U>): KResult<T, U> = when (this) {
        is Err -> op(error)
        is Ok -> ok(this)
    }

}

data class Ok<T, E> internal constructor(val value: T) : KResult<T, E>
data class Err<T, E> internal constructor(val error: E) : KResult<T, E>

fun <T, E> ok(value: T): KResult<T, E> = Ok(value)
fun <T, E> ok(old: Ok<T, *>): KResult<T, E> = old as KResult<T, E>
fun <E> ok(): KResult<Any?, E> = Ok(null)

fun <T, E> err(error: E): KResult<T, E> = Err(error)
fun <T, E> err(old: Err<*, E>): KResult<T, E> = old as KResult<T, E>
fun <T> err(): KResult<T, Any?> = Err(null)

fun <T> T?.asRes(): KResult<T, Any?> = if (this == null) err() else ok(this)
fun <T, E> T?.asRes(error: E): KResult<T, E> = if (this == null) err(error) else ok(this)
